package com.cloud.hello.impl;

import java.security.MessageDigest;
import java.util.Collections;

import com.cloud.common.exception.BusinessException;
import com.cloud.common.exception.CloudException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;

 public class RedisLockUtil {

	private static final Long SUCCESS = 1L;

	/*
	 * 加锁脚本
	 */
	private static final String SCRIPT_LOCK = "if redis.call('setNx', KEYS[1], ARGV[1]) == 1 then redis.call('expire', KEYS[1],ARGV[2]) return 1 else return 0 end";
	/*
	 * 解锁脚本
	 */
	private static final String SCRIPT_UNLOCK = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

	/*
	 * 加锁脚本sha1值
	 */
	private static final String SCRIPT_LOCK_SHA1 = encrypt(SCRIPT_LOCK);
	/*
	 * 解锁脚本sha1值
	 */
	private static final String SCRIPT_UNLOCK_SHA1 = encrypt(SCRIPT_UNLOCK);

	/**
	 * 尝试获取分布式锁
	 * <p>
	 * //	 * @param redisTemplate          Redis客户端
	 *
	 * @param lockKey                锁
	 * @param requestId              请求标识
	 * @param expireTimeMilliseconds 超期时间，多少毫秒后这把锁自动释放
	 * @return 返回true表示拿到锁
	 */
	@SuppressWarnings("unchecked")
	public static boolean tryGetDistributedLock(
			@SuppressWarnings("rawtypes") final RedisTemplate redisTemplate,
			final String lockKey,
			final String requestId,
			final long expireTimeMilliseconds) {

		Object result = redisTemplate.execute(
				new RedisScript<Long>() {
					@Override
					public String getSha1() {
						return SCRIPT_LOCK_SHA1;
					}

					@Override
					public Class<Long> getResultType() {
						return Long.class;
					}

					@Override
					public String getScriptAsString() {
						return SCRIPT_LOCK;
					}

				}, Collections.singletonList(lockKey),// KEYS[1]
				requestId, // ARGV[1]
				expireTimeMilliseconds // ARGV[2]
		);

		return SUCCESS.equals(result);
	}

	/**
	 * 释放分布式锁
	 * <p>
	 * //	 * @param redisTemplate Redis客户端
	 *
	 * @param lockKey   锁
	 * @param requestId 请求标识
	 * @return 返回true表示释放锁成功
	 */
	@SuppressWarnings("unchecked")
	public static boolean releaseDistributedLock(
			@SuppressWarnings("rawtypes") RedisTemplate redisTemplate,
			String lockKey, String requestId) {

		Object result = redisTemplate.execute(new RedisScript<Long>() {
			@Override
			public String getSha1() {
				return SCRIPT_UNLOCK_SHA1;
			}

			@Override
			public Class<Long> getResultType() {
				return Long.class;
			}

			@Override
			public String getScriptAsString() {
				return SCRIPT_UNLOCK;
			}

		}, Collections.singletonList(lockKey), requestId);

		return SUCCESS.equals(result);
	}


	private static final String ZERO = "0";
	private static final String ALGORITHM = "SHA1";

	/**
	 * sha1加密
	 *
	 * @param str
	 * @return 返回十六进制的字符串形式，全部小写
	 */
	private static String encrypt(String str) {
		if (StringUtils.isBlank(str)) {
			return null;
		}
		try {
			MessageDigest messageDigest = MessageDigest.getInstance(ALGORITHM);
			messageDigest.update(str.getBytes());
			return byte2hex(messageDigest.digest());
		} catch (Exception e) {
			e.printStackTrace();
			throw new BusinessException(CloudException.BRAND_NOT_FOUND);
		}
	}

	/**
	 * 二进制转十六进制字符串
	 *
	 * @param bytes
	 * @return
	 */
	private static String byte2hex(byte[] bytes) {
		StringBuilder sign = new StringBuilder();
		for (int i = 0; i < bytes.length; i++) {
			String hex = Integer.toHexString(bytes[i] & 0xFF);
			if (hex.length() == 1) {
				sign.append(ZERO);
			}
			sign.append(hex);
		}
		return sign.toString();
	}


}
